package cn.sskxyz.mongodb.compress;

import java.util.List;
import java.util.Map;

import org.bson.Document;
import org.bson.conversions.Bson;

import com.mongodb.client.ClientSession;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.client.model.InsertOneOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.InsertManyResult;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;

public class CompressMongoCollection<TDocument> extends AbstractMongoCollection<TDocument> {

	private MongoCollection<TDocument> collectionImpl;

	private CompressProcessor compressCodec;

	private List<CompressKey> compressKeys;

	public CompressMongoCollection(MongoCollection<TDocument> collectionImpl, List<CompressKey> compressKeys,
			CompressProcessor compressCodec) {
		this.collectionImpl = collectionImpl;
		this.compressKeys = compressKeys;
		this.compressCodec = compressCodec;
	}

	private void compressDocument(Bson document) {
		if (document instanceof Map) {
			Document doc = (Document) document;
			this.compressCodec.compressValue(doc, compressKeys, getNamespace());
		}
	}

	@Override
	public MongoCollection<TDocument> getMongoCollection() {
		return collectionImpl;
	}

	@Override
	public UpdateResult replaceOne(Bson filter, TDocument replacement, ReplaceOptions replaceOptions) {
		if (replacement instanceof Bson) {
			Document doc = (Document) replacement;
			this.compressDocument(doc);
		}
		return collectionImpl.replaceOne(filter, replacement, replaceOptions);
	}

	@Override
	public UpdateResult replaceOne(ClientSession clientSession, Bson filter, TDocument replacement,
			ReplaceOptions replaceOptions) {
		if (replacement instanceof Bson) {
			Document doc = (Document) replacement;
			this.compressDocument(doc);
		}
		return collectionImpl.replaceOne(clientSession, filter, replacement, replaceOptions);
	}

	@Override
	public UpdateResult updateOne(Bson filter, Bson update, UpdateOptions updateOptions) {
		this.compressDocument(update);
		return collectionImpl.updateOne(filter, update, updateOptions);
	}

	@Override
	public UpdateResult updateOne(ClientSession clientSession, Bson filter, Bson update, UpdateOptions updateOptions) {
		this.compressDocument(update);
		return collectionImpl.updateOne(clientSession, filter, update, updateOptions);
	}

	@Override
	public UpdateResult updateOne(Bson filter, List<? extends Bson> update, UpdateOptions updateOptions) {
		return collectionImpl.updateOne(filter, update, updateOptions);
	}

	@Override
	public UpdateResult updateMany(Bson filter, Bson update, UpdateOptions updateOptions) {
		this.compressDocument(update);
		return collectionImpl.updateMany(filter, update, updateOptions);
	}

	@Override
	public UpdateResult updateMany(ClientSession clientSession, Bson filter, Bson update, UpdateOptions updateOptions) {
		this.compressDocument(update);
		return collectionImpl.updateMany(clientSession, filter, update, updateOptions);
	}

	/**
	 * @param update pipeline
	 */
	@Override
	public UpdateResult updateMany(Bson filter, List<? extends Bson> update, UpdateOptions updateOptions) {
		return collectionImpl.updateMany(filter, update, updateOptions);
	}

	@Override
	public <TResult> FindIterable<TResult> find(Bson filter, Class<TResult> resultClass) {
		FindIterable<TResult> iter = collectionImpl.find(filter, resultClass);
		return new CompressFindIterable<TResult>(iter, compressKeys, collectionImpl.getNamespace(), compressCodec);
	}

	@Override
	public <TResult> FindIterable<TResult> find(ClientSession clientSession, Bson filter, Class<TResult> resultClass) {
		FindIterable<TResult> iter = collectionImpl.find(clientSession, filter, resultClass);
		return new CompressFindIterable<TResult>(iter, compressKeys, collectionImpl.getNamespace(), compressCodec);
	}

	@Override
	public InsertOneResult insertOne(TDocument document, InsertOneOptions options) {
		if (document instanceof Bson) {
			this.compressDocument((Bson) document);
		}
		return collectionImpl.insertOne(document, options);
	}

	@Override
	public InsertOneResult insertOne(ClientSession clientSession, TDocument document, InsertOneOptions options) {
		if (document instanceof Bson) {
			this.compressDocument((Bson) document);
		}
		return collectionImpl.insertOne(clientSession, document, options);
	}

	@Override
	public InsertManyResult insertMany(List<? extends TDocument> documents, InsertManyOptions options) {
		documents.forEach(doc -> {
			if (doc instanceof Bson) {
				this.compressDocument((Bson) doc);
			}
		});

		return collectionImpl.insertMany(documents, options);
	}

	@Override
	public InsertManyResult insertMany(ClientSession clientSession, List<? extends TDocument> documents,
			InsertManyOptions options) {
		documents.forEach(doc -> {
			if (doc instanceof Bson) {
				this.compressDocument((Bson) doc);
			}
		});

		return collectionImpl.insertMany(clientSession, documents, options);
	}

	@Override
	public TDocument findOneAndReplace(Bson filter, TDocument replacement, FindOneAndReplaceOptions options) {
		if (replacement instanceof Bson) {
			this.compressDocument((Bson) replacement);
		}

		return collectionImpl.findOneAndReplace(filter, replacement, options);
	}

	@Override
	public TDocument findOneAndReplace(ClientSession clientSession, Bson filter, TDocument replacement,
			FindOneAndReplaceOptions options) {
		if (replacement instanceof Bson) {
			this.compressDocument((Bson) replacement);
		}

		return collectionImpl.findOneAndReplace(clientSession, filter, replacement, options);
	}

	@Override
	public TDocument findOneAndUpdate(Bson filter, Bson update, FindOneAndUpdateOptions options) {
		this.compressDocument(update);
		return collectionImpl.findOneAndUpdate(filter, update, options);
	}

	@Override
	public TDocument findOneAndUpdate(ClientSession clientSession, Bson filter, Bson update,
			FindOneAndUpdateOptions options) {
		this.compressDocument(update);
		return collectionImpl.findOneAndUpdate(clientSession, filter, update, options);
	}
}
